package com.time;

import com.onelogin.saml2.Auth;
import com.onelogin.saml2.servlet.ServletUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author yonghao.tang
 */

@WebServlet("/callback-shuchen")
public class CallbackServlet2 extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(CallbackServlet2.class);
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            //构造Auth客户端，默认使用classpath下的onelogin.saml.properties，也可指定文件
            Auth auth = new Auth("shuchen.saml.properties", request, response);
            //处理响应,解析断言
            auth.processResponse();
            
            response.setContentType("text/html;charset=UTF-8");
            
            //检查当前用户是否已通过SAML身份验证，可以用于验证SAML会话是否有效以及用户是否已经成功登录
            if (!auth.isAuthenticated()) {
                log.error("用户身份验证失败!");
                response.getWriter().println("登陆失败！");
            } else {
                //到这一步，说明用户身份验证通过，算登陆成功
                response.getWriter().println("登陆成功！");
                
                //用户身份验证通过，看是否有其他错误信息，这个步骤是可选的
                List<String> errors = auth.getErrors();
                if (!errors.isEmpty() && Boolean.TRUE.equals(auth.isDebugActive())) {
                    String errorReason = auth.getLastErrorReason();
                    if (!StringUtils.isEmpty(errorReason)) {
                        log.error("用户身份验证完成，但又其他错误信息:[{}]", errorReason);
                        response.getWriter().println("登陆失败:" + errorReason);
                    }
                }
                
                //获取当前用户主键信息，SAML协议中默认为用户登陆账号，在okta应用上可以配置是账号还是邮箱
                String nameId = auth.getNameId();
                String nameIdFormat = auth.getNameIdFormat();
                String sessionIndex = auth.getSessionIndex();
                String nameidNameQualifier = auth.getNameIdNameQualifier();
                String nameidSPNameQualifier = auth.getNameIdSPNameQualifier();
                log.info("SSO success! 当前会话用户:[{}]", nameId);
                log.info("当前会话用户:[{}],sessionIndex:[{}],nameIdFormat:[{}]", nameId, sessionIndex, nameIdFormat);
                log.info("当前会话用户:[{}],nameidNameQualifier:[{}],nameidSPNameQualifier:[{}]", nameId, nameidNameQualifier, nameidSPNameQualifier);
                
                //获取其他额外的属性，这个可以在okta上配置
                Map<String, List<String>> attributes = auth.getAttributes();
                if (attributes.isEmpty()) {
                    log.info("当前会话用户:[{}],没有配置其他属性", nameId);
                } else {
                    Set<Map.Entry<String, List<String>>> entries = attributes.entrySet();
                    for (Map.Entry<String, List<String>> entry : entries) {
                        log.info("当前会话用户:[{}] attribute {} : {}", nameId, entry.getKey(), entry.getValue());
                    }
                }
                
                HttpSession session = request.getSession();
                session.setAttribute("currentUserSession", sessionIndex);
                session.setAttribute("currentUser", nameId);
                
                //如果有重定向URL，则可以继续将访问该URL，这个作用是用户访问资源前没有登陆，可以再登陆后继续访问原来的链接
                String relayState = request.getParameter("RelayState");
                log.info("当前会话用户:[{}],RelayState:[{}]", nameId, relayState);
                if (relayState != null && !relayState.equals(ServletUtils.getSelfRoutedURLNoQuery(request))) {
                    //如果用户访问时未登陆，则登陆成功后，重定向到用户之前访问的资源URL，包括参数
                    if (!relayState.contains("callback") && !relayState.contains("login")) {
                        //避免递归
                        response.sendRedirect(relayState);
                    }
                }
            }
        } catch (Exception e) {
            log.error("login with shuchen's okta callback failed", e);
        }
    }
}
